﻿using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using Microsoft.EntityFrameworkCore.Query.Internal;
using Microsoft.EntityFrameworkCore.Storage;
using System.Linq;
using System.Reflection;

namespace Batman.Core
{
    public static class RepositoryExtension
    {
        /// <summary>
        /// 获取查询句柄
        /// 为了不写query.GetAll().AsNoTracking而写这个方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="repo"></param>
        /// <param name="asNoTracking"></param>
        /// <returns></returns>
        public static IQueryable<T> GetQuery<T>(this IRepository<T, string> repo, bool asNoTracking = true) where T : Entity<string>
        {
            IQueryable<T> query = repo.GetAll();
            if (asNoTracking) query = query.AsNoTracking();
            return query;
        }

        /// <summary>
        /// 根据ID查询出实体数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="repo"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static T GetById<T>(this IRepository<T, string> repo, string id)
            where T : Entity<string>
        {
            return repo.GetQuery().FirstOrDefault(t => t.Id == id);
        }

        /// <summary>
        /// 根据ID查询出实体数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="repo"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public static T GetById<T, TPropery>(this IIncludableQueryable<T, TPropery> queryable, string id)
            where T : Entity<string>
        {
            return queryable.FirstOrDefault(t => t.Id == id);
        }

        #region 获取EF CORE生成的SQL

        private static readonly TypeInfo QueryCompilerTypeInfo = typeof(QueryCompiler).GetTypeInfo();

        private static readonly FieldInfo QueryCompilerField = typeof(EntityQueryProvider).GetTypeInfo().DeclaredFields.First(x => x.Name == "_queryCompiler");

        private static readonly FieldInfo QueryModelGeneratorField = QueryCompilerTypeInfo.DeclaredFields.First(x => x.Name == "_queryModelGenerator");

        private static readonly FieldInfo DataBaseField = QueryCompilerTypeInfo.DeclaredFields.Single(x => x.Name == "_database");

        private static readonly PropertyInfo DatabaseDependenciesField = typeof(Database).GetTypeInfo().DeclaredProperties.Single(x => x.Name == "Dependencies");

        /// <summary>
        /// 获取生产的SQL
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="query"></param>
        /// <returns></returns>
        public static string ToSql<TEntity>(this IQueryable<TEntity> query) where TEntity : class
        {
            var queryCompiler = (QueryCompiler)QueryCompilerField.GetValue(query.Provider);
            var modelGenerator = (QueryModelGenerator)QueryModelGeneratorField.GetValue(queryCompiler);
            var queryModel = modelGenerator.ParseQuery(query.Expression);
            var database = (IDatabase)DataBaseField.GetValue(queryCompiler);
            var databaseDependencies = (DatabaseDependencies)DatabaseDependenciesField.GetValue(database);
            var queryCompilationContext = databaseDependencies.QueryCompilationContextFactory.Create(false);
            var modelVisitor = (RelationalQueryModelVisitor)queryCompilationContext.CreateQueryModelVisitor();
            modelVisitor.CreateQueryExecutor<TEntity>(queryModel);
            var sql = modelVisitor.Queries.First().ToString();

            return sql;
        }

        #endregion

    }
}
